import type { HttpResponseData } from '@/types/api'
import type {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'
import axios from 'axios'

// 自定义的请求交互配置
export interface CustomAxiosRequestConfig<D = unknown> extends AxiosRequestConfig<D> {
  // 是否关闭请求失败的报错提示；
  closeShowToast?: boolean
}

export interface CustomAxiosResponse<D = unknown> extends AxiosResponse<D> {
  config: InternalAxiosRequestConfig<D> & CustomAxiosRequestConfig<D>
}

export function httpErrorCodeMessage(code: number): string {
  let message = ''
  switch (code) {
    case 400:
      message = '请求错误(400)'
      break
    case 401:
      message = '未授权，请重新登录(401)'
      // todo 清空登录信息
      break
    case 403:
      message = '拒绝访问(403)'
      break
    case 404:
      message = '请求出错(404)'
      break
    case 408:
      message = '请求超时(408)'
      break
    case 500:
      message = '服务器错误(500)'
      break
    case 501:
      message = '服务未实现(501)'
      break
    case 502:
      message = '网络错误(502)'
      break
    case 503:
      message = '服务不可用(503)'
      break
    case 504:
      message = '网络超时(504)'
      break
    case 505:
      message = 'HTTP版本不受支持(505)'
      break
    default:
      message = `连接出错(${code})!`
  }
  return message
}

export class Request {
  instance: AxiosInstance
  // 基础配置，url和超时时间
  baseConfig: CustomAxiosRequestConfig = { baseURL: import.meta.env.VITE_HOST, timeout: 60000 }

  constructor(config: CustomAxiosRequestConfig) {
    this.instance = axios.create(Object.assign(this.baseConfig, config))

    this.instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        // todo 获取token
        // const accessToken = ''
        // todo请求签名添加sign
        // config.headers!.sign = ''
        // todo请求头添加token
        // config.headers!.authorization = accessToken && config.url != '/oauth/token' ? `bearer ${accessToken}` : `Basic todo`
        // 请求签名end
        return config
      },
      (err: unknown) => {
        return Promise.reject(err)
      },
    )

    this.instance.interceptors.response.use(
      (res: CustomAxiosResponse) => res,
      (err: { response: string }) => {
        // todo 弹出提示报错
        return Promise.reject(err.response)
      },
    )
  }

  // todo格式化返回数据
  private formatResponse<T>(res: CustomAxiosResponse<HttpResponseData<T>>): HttpResponseData<T> {
    // const isSuccess = res.data.code === 0 && res.data.status === 200
    const isSuccess = res.status === 200 && res.data.code === 1
    // 错误处理
    if (!isSuccess) {
      let message: string = res.data.message || res.data.msg || ''
      if (res.data.status === 401) {
        message = httpErrorCodeMessage(401)
      }
      console.error(message)
      // todo 提示报错
    }
    // 统一返回
    return {
      isSuccess: isSuccess,
      code: res.data.code,
      message: res.data.message || res.data.msg || '',
      data: res.data.data,
      status: res.data.status,
      serverTime: res.data.serverTime,
    }
  }

  private formatErrorResponse<T>(error: {
    status: number
    statusText: string
  }): HttpResponseData<T> {
    return {
      isSuccess: false,
      code: error?.status || 500,
      message: error?.statusText || 'Unknown Error',
      data: null as T,
    }
  }

  public request<T = unknown>(config: CustomAxiosRequestConfig): Promise<HttpResponseData<T>> {
    return this.instance
      .request<HttpResponseData<T>>(config)
      .then((res) => this.formatResponse(res))
      .catch((err) => Promise.reject(this.formatErrorResponse(err)))
  }

  public get<T = unknown>(
    url: string,
    config?: CustomAxiosRequestConfig,
  ): Promise<HttpResponseData<T>> {
    return this.instance
      .get<HttpResponseData<T>>(url, config)
      .then((res) => this.formatResponse(res))
      .catch((err) => Promise.reject(this.formatErrorResponse(err)))
  }

  public post<T = unknown>(
    url: string,
    data?: unknown,
    config?: CustomAxiosRequestConfig,
  ): Promise<HttpResponseData<T>> {
    return this.instance
      .post<HttpResponseData<T>>(url, data, config)
      .then((res) => this.formatResponse(res))
      .catch((err) => Promise.reject(this.formatErrorResponse(err)))
  }
}

export default new Request({})
